在WinForm中.NET Framework提供的TextBox是不能修改边框的颜色的,在如今对界面要求越来越高的情况下只有黑色边框的TextBox显然不能满人们的需求。本文将介绍一种修改TextBox边框颜色且不会闪烁的最佳做法。
最开始的时候,我还是像万博manbext首页其他控件一样重写TextBox的OnPaint()方法,结果发现其根本就不走OnPaint()方法。因为有些控件是由系统进程绘制的,重写OnPaint()方法不起作用,TextBox就是其中之一。
面对以上问题,网上也有很多答案,自己都不太满意,但总体只有两种做法:
1. 隐藏TextBox的边框并将其放在一个容器中,设置容器的背景颜色来达到边框的效果。
2. 重写WndProc()方法,拦截系统消息来绘制自己想要颜色的边框。
这两种方法都有缺陷:
1. 每个TextBox都多一个容器控件,当TextBox较多的时候,加载较慢且浪费系统资源。
2. 鼠标在TextBox上来回滑动或者是点击TextBox的时候,会闪烁。
那么有没有一种方法既不占用系统资源又不闪烁呢?答案是有的。
我们只需要重写TextBox的CreateParams属性,给其加上一个边框,然后绘制自己想要的即可。以下是代码:
绘制过程中会用到一些API和消息常量等先声明
public class NativeMethods { internal const int WS_EX_CLIENTEDGE = 512 /*0x0200*/; internal const int WS_EX_WINDOWEDGE = 0x0100; internal const int WM_PAINT = 15; // 0x000f internal const int WM_NCPAINT = 133; // 0x0085 [StructLayout(LayoutKind.Sequential)] public struct RECT { public int left; public int top; public int right; public int bottom; public RECT(Rectangle rect) { this.bottom = rect.Bottom; this.left = rect.Left; this.right = rect.Right; this.top = rect.Top; } public RECT(int left, int top, int right, int bottom) { this.bottom = bottom; this.left = left; this.right = right; this.top = top; } public static RECT FromXYWH(int x, int y, int width, int height) { return new RECT(x, y, x + width, y + height); } public int Width { get { return this.right - this.left; } } public int Height { get { return this.bottom - this.top; } } public override /*Object*/ string ToString() { return String.Concat( "Left = ", this.left, " Top ", this.top, " Right = ", this.right, " Bottom = ", this.bottom); } public static implicit operator Rectangle(RECT rect) { return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); } } [DllImport("user32.dll")] internal static extern bool GetWindowRect(IntPtr hwnd, ref RECT lpRect); [DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] internal static extern IntPtr CreateRectRgn(int x1, int y1, int x2, int y2); [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] internal static extern IntPtr GetWindowDC(IntPtr hWnd); [DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] internal static extern IntPtr CreateCompatibleDC(IntPtr hDC); [DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] internal static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int width, int height); [DllImport("gdi32", CharSet = CharSet.Auto, ExactSpelling = true)] internal static extern IntPtr SelectObject(IntPtr hdc, IntPtr hObject); [DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] internal static extern int CombineRgn(IntPtr hRgn, IntPtr hRgn1, IntPtr hRgn2, int nCombineMode); [DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] public static extern int OffsetRgn(IntPtr hrgn, int nXOffset, int nYOffset); [DllImport("gdi32")] internal static extern bool DeleteObject(IntPtr hObject); [DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] internal static extern int SelectClipRgn(IntPtr hDC, IntPtr hRgn); [DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)] public static extern int ExcludeClipRect(IntPtr hdc, int nLeft, int nTop, int nRight, int nBottom); [DllImport("gdi32.dll")] internal static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, Int32 dwRop); [DllImport("gdi32", EntryPoint = "DeleteDC", CharSet = CharSet.Auto, ExactSpelling = true)] internal static extern bool DeleteDC(IntPtr hDC); }
添加一个组件CustomTextBox继承TextBox,实现自定义边框颜色
public partial class CustomTextBox : System.Windows.Forms.TextBox { private Rectangle m_rcClient = Rectangle.Empty; private Color _coBorder = Color.Red; public CustomTextBox() { InitializeComponent(); } protected override CreateParams CreateParams { get { CreateParams cparams = base.CreateParams; BorderStyle border = this.BorderStyle; if (!(border == BorderStyle.Fixed3D)) { cparams.ExStyle &= ~NativeMethods.WS_EX_CLIENTEDGE; cparams.Style &= ~8388608; switch (border) { // Unlike other controls, text box doesn't draw its single border in the NC area! case BorderStyle.Fixed3D: case BorderStyle.FixedSingle: // 使一个视窗具有凹陷边框 cparams.ExStyle = cparams.ExStyle | NativeMethods.WS_EX_CLIENTEDGE | NativeMethods.WS_EX_WINDOWEDGE; break; } } return cparams; } } public Color BorderColor { get { return this._coBorder; } set { this._coBorder = value; } } protected override void OnPaint(PaintEventArgs pe) { base.OnPaint(pe); } protected override void WndProc(ref Message m) { switch (m.Msg) { case NativeMethods.WM_PAINT: this.OnWmNcPaint(ref m); break; case NativeMethods.WM_NCPAINT: this.OnWmNcPaint(ref m); return; } base.WndProc(ref m); } private void OnWmNcPaint(ref Message m) { NativeMethods.RECT rcWnd = new NativeMethods.RECT(); NativeMethods.GetWindowRect(m.HWnd, ref rcWnd); int intWidth = rcWnd.Width; int intHeight = rcWnd.Height; int width = rcWnd.Width; int height = rcWnd.Height; IntPtr hRgn = NativeMethods.CreateRectRgn(0, 0, width, height); if (hRgn != IntPtr.Zero) { IntPtr hDc = NativeMethods.GetWindowDC(m.HWnd); if (hDc != IntPtr.Zero) { IntPtr memDc = NativeMethods.CreateCompatibleDC(hDc); if (memDc != IntPtr.Zero) { IntPtr hBmp = NativeMethods.CreateCompatibleBitmap(hDc, width, height); if (hBmp != IntPtr.Zero) { IntPtr hObj = NativeMethods.SelectObject(memDc, hBmp); if (m.WParam.ToInt32() != 1) { IntPtr tmpRgn = NativeMethods.CreateRectRgn(0, 0, 0, 0); NativeMethods.CombineRgn(tmpRgn, m.WParam, IntPtr.Zero, NativeMethods.RGN_COPY); NativeMethods.OffsetRgn(tmpRgn, -rcWnd.left, -rcWnd.top); NativeMethods.CombineRgn(hRgn, hRgn, tmpRgn, NativeMethods.RGN_AND); NativeMethods.DeleteObject(tmpRgn); } using (Graphics g = Graphics.FromHdc(memDc)) { RenderNCArea(g, width, height, true); } NativeMethods.SelectClipRgn(hDc, hRgn); NativeMethods.ExcludeClipRect(hDc, m_rcClient.X, m_rcClient.Y, m_rcClient.Right, m_rcClient.Bottom); NativeMethods.BitBlt(hDc, 0, 0, rcWnd.Width, rcWnd.Height, memDc, 0, 0, 0x00CC0020/*SRCCOPY*/ ); NativeMethods.SelectObject(memDc, hObj); NativeMethods.DeleteObject(hBmp); } NativeMethods.DeleteDC(memDc); } NativeMethods.DeleteDC(hDc); } IntPtr hTmp = NativeMethods.CreateRectRgn(m_rcClient.Left, m_rcClient.Top, m_rcClient.Right, m_rcClient.Bottom); if (hTmp != IntPtr.Zero) { NativeMethods.CombineRgn(hRgn, hRgn, hTmp, NativeMethods.RGN_AND); NativeMethods.OffsetRgn(hRgn, rcWnd.left, rcWnd.top); NativeMethods.DeleteObject(hTmp); Message msg = new Message(); msg.Msg = m.Msg; msg.HWnd = m.HWnd; msg.WParam = hRgn; base.WndProc(ref msg); } NativeMethods.DeleteObject(hRgn); } m.Result = IntPtr.Zero; } private void RenderNCArea(Graphics g, int width, int height, bool bDrawBorder) { Rectangle rc = new Rectangle(0, 0, width, height); using (Brush brush = new SolidBrush(this.BackColor)) { g.FillRectangle(brush, 0, 0, width, height); } if (bDrawBorder) { DrawBorders(rc, g); } } private void DrawBorders(Rectangle rc, Graphics g) { ControlPaint.DrawBorder(g, rc, this.BorderColor, ButtonBorderStyle.Solid); } }
效果如图:
本文章由创风网原创,转载请注明出处:http://www.windite.com/article/details/ftw0yz5